
package com.ami.kvm.jviewer.avistream;

import com.ami.kvm.jviewer.common.IAVIOutputStream;
import com.ami.kvm.jviewer.common.IAVIOutputStream.VideoFormat;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;

public class AVIOutputStream implements IAVIOutputStream {
	private ImageOutputStream out;
	private IAVIOutputStream.VideoFormat videoFormat;
	private float quality = 0.9F;

	private Date creationTime;

	private int imgWidth = -1;

	private int imgHeight = -1;

	private long timeScale = 1L;

	private long frameRate = 30L;

	public AVIOutputStream() {
	}

	private static enum States {
		STARTED, FINISHED, CLOSED;

		private States() {
		}
	}

	private States state = States.FINISHED;

	public boolean streamClosed = false;
	public boolean singleVideo = false;

	private LinkedList<Sample> videoFrames;

	private CompositeChunk aviChunk;

	private CompositeChunk moviChunk;

	FixedSizeDataChunk avihChunk;

	FixedSizeDataChunk strhChunk;

	FixedSizeDataChunk strfChunk;

	private static class Sample {
		long offset;

		long length;

		long duration;

		public Sample(long paramLong1, long paramLong2, long paramLong3) {
			this.duration = paramLong1;
			this.offset = paramLong2;
			this.length = paramLong3;
		}
	}

	private abstract class Chunk {
		protected String chunkType;

		protected long offset;

		public Chunk(String paramString) throws IOException {
			this.chunkType = paramString;
			this.offset = AVIOutputStream.this.out.getStreamPosition();
		}

		public abstract void finish() throws IOException;

		public abstract long size();
	}

	private class CompositeChunk extends AVIOutputStream.Chunk {
		protected String compositeType;

		private LinkedList<AVIOutputStream.Chunk> children;

		private boolean finished;

		public CompositeChunk(String paramString1, String paramString2) throws IOException {
			super(paramString2);
			this.compositeType = paramString1;

			AVIOutputStream.this.out.writeLong(0L);
			AVIOutputStream.this.out.writeInt(0);
			this.children = new LinkedList();
		}

		public void add(AVIOutputStream.Chunk paramChunk) throws IOException {
			if (this.children.size() > 0) {
				((AVIOutputStream.Chunk) this.children.getLast()).finish();
			}
			this.children.add(paramChunk);
		}

		public void finish() throws IOException {
			if (!this.finished) {
				if (size() > 4294967295L) {
					throw new IOException("CompositeChunk \"" + this.chunkType + "\" is too large: " + size());
				}

				long l = AVIOutputStream.this.out.getStreamPosition();
				AVIOutputStream.this.out.seek(this.offset);

				DataChunkOutputStream localDataChunkOutputStream = new DataChunkOutputStream(
						new FilterImageOutputStream(AVIOutputStream.this.out));
				localDataChunkOutputStream.writeType(this.compositeType);
				localDataChunkOutputStream.writeUInt(size() - 8L);
				localDataChunkOutputStream.writeType(this.chunkType);
				for (AVIOutputStream.Chunk localChunk : this.children) {
					localChunk.finish();
				}
				AVIOutputStream.this.out.seek(l);
				if (size() % 2L == 1L) {
					AVIOutputStream.this.out.writeByte(0);
				}
				this.finished = true;
			}
		}

		public long size() {
			long l = 12L;
			for (AVIOutputStream.Chunk localChunk : this.children) {
				l += localChunk.size() + localChunk.size() % 2L;
			}
			return l;
		}
	}

	private class DataChunk extends AVIOutputStream.Chunk {
		private DataChunkOutputStream data;

		private boolean finished;

		public DataChunk(String paramString) throws IOException {
			super(paramString);
			AVIOutputStream.this.out.writeLong(0L);
			this.data = new DataChunkOutputStream(new FilterImageOutputStream(AVIOutputStream.this.out));
		}

		public DataChunkOutputStream getOutputStream() {
			if (this.finished) {
				throw new IllegalStateException("DataChunk is finished");
			}
			return this.data;
		}

		public long getOffset() {
			return this.offset;
		}

		public void finish() throws IOException {
			if (!this.finished) {
				long l1 = size();

				if (size() > 4294967295L) {
					throw new IOException("DataChunk \"" + this.chunkType + "\" is too large: " + size());
				}

				long l2 = AVIOutputStream.this.out.getStreamPosition();
				AVIOutputStream.this.out.seek(this.offset);

				DataChunkOutputStream localDataChunkOutputStream = new DataChunkOutputStream(
						new FilterImageOutputStream(AVIOutputStream.this.out));
				localDataChunkOutputStream.writeType(this.chunkType);
				localDataChunkOutputStream.writeUInt(size() - 8L);
				AVIOutputStream.this.out.seek(l2);
				if (size() % 2L == 1L) {
					AVIOutputStream.this.out.writeByte(0);
				}
				this.finished = true;
				long l3 = size();
				if (l1 != l3) {
					System.err.println("size mismatch " + l1 + ".." + l3);
				}
			}
		}

		public long size() {
			return 8L + this.data.size();
		}
	}

	private class FixedSizeDataChunk extends AVIOutputStream.Chunk {
		private DataChunkOutputStream data;

		private boolean finished;

		private long fixedSize;

		public FixedSizeDataChunk(String paramString, long paramLong) throws IOException {
			super(paramString);
			this.fixedSize = paramLong;
			this.data = new DataChunkOutputStream(new FilterImageOutputStream(AVIOutputStream.this.out));
			this.data.writeType(paramString);
			this.data.writeUInt(paramLong);
			this.data.clearCount();

			byte[] arrayOfByte = new byte[(int) Math.min(512L, paramLong)];
			long l = 0L;
			while (l < paramLong) {
				this.data.write(arrayOfByte, 0, (int) Math.min(arrayOfByte.length, paramLong - l));
				l += Math.min(arrayOfByte.length, paramLong - l);
			}
			if (paramLong % 2L == 1L) {
				AVIOutputStream.this.out.writeByte(0);
			}
			seekToStartOfData();
		}

		public DataChunkOutputStream getOutputStream() {
			return this.data;
		}

		public long getOffset() {
			return this.offset;
		}

		public void seekToStartOfData() throws IOException {
			AVIOutputStream.this.out.seek(this.offset + 8L);
			this.data.clearCount();
		}

		public void seekToEndOfChunk() throws IOException {
			AVIOutputStream.this.out.seek(this.offset + 8L + this.fixedSize + this.fixedSize % 2L);
		}

		public void finish() throws IOException {
			if (!this.finished) {
				this.finished = true;
			}
		}

		public long size() {
			return 8L + this.fixedSize;
		}
	}

	public AVIOutputStream(File paramFile, IAVIOutputStream.VideoFormat paramVideoFormat, boolean paramBoolean)
			throws IOException {
		if (paramVideoFormat == null) {
			throw new IllegalArgumentException("format must not be null");
		}

		if (paramFile.exists()) {
			paramFile.delete();
		}
		this.out = new FileImageOutputStream(paramFile);
		this.videoFormat = paramVideoFormat;
		this.videoFrames = new LinkedList();
		this.singleVideo = paramBoolean;
	}

	public AVIOutputStream(ImageOutputStream paramImageOutputStream, IAVIOutputStream.VideoFormat paramVideoFormat) {
		if (paramVideoFormat == null) {
			throw new IllegalArgumentException("format must not be null");
		}
		this.out = paramImageOutputStream;
		this.videoFormat = paramVideoFormat;
		this.videoFrames = new LinkedList();
	}

	public void setTimeScale(long paramLong) {
		if (paramLong <= 0L) {
			paramLong = 1L;
		}
		this.timeScale = paramLong;
	}

	public long getTimeScale() {
		return this.timeScale;
	}

	public void setFrameRate(long paramLong) {
		if (paramLong <= 0L) {
			paramLong = 1L;
		}
		if (this.state == States.STARTED) {
			throw new IllegalStateException("frameDuration must be set before the first frame is written");
		}
		this.frameRate = paramLong;
	}

	public long getFrameRate() {
		return this.frameRate;
	}

	public void setVideoCompressionQuality(float paramFloat) {
		this.quality = paramFloat;
	}

	public float getVideoCompressionQuality() {
		return this.quality;
	}

	public void setVideoDimension(int paramInt1, int paramInt2) {
		if ((paramInt1 < 1) || (paramInt2 < 1)) {
			throw new IllegalArgumentException("width and height must be greater zero.");
		}
		this.imgWidth = paramInt1;
		this.imgHeight = paramInt2;
	}

	public Dimension getVideoDimension() {
		if ((this.imgWidth < 1) || (this.imgHeight < 1)) {
			return null;
		}
		return new Dimension(this.imgWidth, this.imgHeight);
	}

	private void ensureStarted() throws IOException {
		if (this.state != States.STARTED) {
			this.creationTime = new Date();
			writeProlog();
			this.state = States.STARTED;
		}
	}

	public boolean writeFrame(BufferedImage paramBufferedImage) throws IOException {
		ensureOpen();
		ensureStarted();

		if (this.imgWidth == -1) {
			this.imgWidth = paramBufferedImage.getWidth();
			this.imgHeight = paramBufferedImage.getHeight();
		} else if (!this.singleVideo) {
			if ((this.imgWidth != paramBufferedImage.getWidth())
					|| (this.imgHeight != paramBufferedImage.getHeight())) {

				close();
				this.imgHeight = -1;
				this.imgWidth = -1;
				this.streamClosed = true;
				return false;
			}
		}

		DataChunk localDataChunk = new DataChunk(
				this.videoFormat == IAVIOutputStream.VideoFormat.RAW ? "00db" : "00dc");
		this.moviChunk.add(localDataChunk);
		long l1 = this.out.getStreamPosition();
		ImageWriter localImageWriter = (ImageWriter) ImageIO.getImageWritersByMIMEType("image/jpeg").next();
		ImageWriteParam localImageWriteParam = localImageWriter.getDefaultWriteParam();
		localImageWriteParam.setCompressionMode(2);
		localImageWriteParam.setCompressionQuality(this.quality);
		MemoryCacheImageOutputStream localMemoryCacheImageOutputStream = new MemoryCacheImageOutputStream(
				localDataChunk.getOutputStream());
		localImageWriter.setOutput(localMemoryCacheImageOutputStream);
		IIOImage localIIOImage = new IIOImage(paramBufferedImage, null, null);
		localImageWriter.write(null, localIIOImage, localImageWriteParam);
		localImageWriter.dispose();
		long l2 = this.out.getStreamPosition() - l1;
		localDataChunk.finish();

		this.videoFrames.add(new Sample(this.frameRate, l1, l2));
		if (this.out.getStreamPosition() > 4294967296L) {
			throw new IOException("AVI file is larger than 4 GB");
		}
		return true;
	}

	public void writeFrame(File paramFile) throws IOException {
		FileInputStream localFileInputStream = null;
		try {
			localFileInputStream = new FileInputStream(paramFile);
			writeFrame(localFileInputStream);
		} finally {
			if (localFileInputStream != null) {
				localFileInputStream.close();
			}
		}
	}

	public void writeFrame(InputStream paramInputStream) throws IOException {
		ensureOpen();
		ensureStarted();

		DataChunk localDataChunk = new DataChunk("00dc");
		this.moviChunk.add(localDataChunk);
		DataChunkOutputStream localDataChunkOutputStream = localDataChunk.getOutputStream();
		long l1 = this.out.getStreamPosition();
		byte[] arrayOfByte = new byte['Ȁ'];
		int i;
		while ((i = paramInputStream.read(arrayOfByte)) != -1) {
			localDataChunkOutputStream.write(arrayOfByte, 0, i);
		}
		long l2 = this.out.getStreamPosition() - l1;
		localDataChunk.finish();
		this.videoFrames.add(new Sample(this.frameRate, l1, l2));
		if (this.out.getStreamPosition() > 4294967296L) {
			throw new IOException("AVI file is larger than 4 GB");
		}
	}

	public void close() throws IOException {
		if (this.state == States.STARTED) {
			finish();
		}
		if (this.state != States.CLOSED) {
			this.out.close();
			this.state = States.CLOSED;
		}
	}

	public void finish() throws IOException {
		ensureOpen();
		if (this.state != States.FINISHED) {
			if ((this.imgWidth == -1) || (this.imgHeight == -1)) {
				throw new IllegalStateException("image width and height must be specified");
			}

			this.moviChunk.finish();
			writeEpilog();
			this.state = States.FINISHED;
			this.imgWidth = (this.imgHeight = -1);
		}
	}

	private void ensureOpen() throws IOException {
		if (this.state == States.CLOSED) {
			throw new IOException("Stream closed");
		}
	}

	private void writeProlog() throws IOException {
		this.aviChunk = new CompositeChunk("RIFF", "AVI ");
		CompositeChunk localCompositeChunk1 = new CompositeChunk("LIST", "hdrl");

		this.aviChunk.add(localCompositeChunk1);
		this.avihChunk = new FixedSizeDataChunk("avih", 56L);
		this.avihChunk.seekToEndOfChunk();
		localCompositeChunk1.add(this.avihChunk);

		CompositeChunk localCompositeChunk2 = new CompositeChunk("LIST", "strl");
		localCompositeChunk1.add(localCompositeChunk2);

		this.strhChunk = new FixedSizeDataChunk("strh", 56L);
		this.strhChunk.seekToEndOfChunk();
		localCompositeChunk2.add(this.strhChunk);
		this.strfChunk = new FixedSizeDataChunk("strf", 40L);
		this.strfChunk.seekToEndOfChunk();
		localCompositeChunk2.add(this.strfChunk);

		this.moviChunk = new CompositeChunk("LIST", "movi");
		this.aviChunk.add(this.moviChunk);
	}

	private void writeEpilog() throws IOException {
		int i = 0;
		for (Sample localSample1 : this.videoFrames) {
			i = (int) (i + localSample1.duration);
		}
		long l = 0L;
		for (Object localObject1 = this.videoFrames.iterator(); ((Iterator) localObject1).hasNext();) {
			Sample localObject2 = (Sample) ((Iterator) localObject1).next();
			if (((Sample) localObject2).length > l) {
				l = ((Sample) localObject2).length;
			}
		}

		Object localObject2 = new DataChunk("idx1");
		this.aviChunk.add((Chunk) localObject2);
		DataChunkOutputStream localObject1 = ((DataChunk) localObject2).getOutputStream();

		for (Sample localSample2 : this.videoFrames) {
			((DataChunkOutputStream) localObject1).writeType("00dc");

			((DataChunkOutputStream) localObject1).writeUInt(16L);

			((DataChunkOutputStream) localObject1).writeUInt(localSample2.offset - this.moviChunk.offset - 16L);

			((DataChunkOutputStream) localObject1).writeUInt(localSample2.length);
		}

		((DataChunk) localObject2).finish();

		this.avihChunk.seekToStartOfData();
		localObject1 = this.avihChunk.getOutputStream();

		((DataChunkOutputStream) localObject1).writeUInt(10000000L * this.timeScale / this.frameRate);

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUInt(16L);

		((DataChunkOutputStream) localObject1).writeUInt(this.videoFrames.size());

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUInt(1L);

		((DataChunkOutputStream) localObject1).writeUInt(l);

		((DataChunkOutputStream) localObject1).writeUInt(this.imgWidth);

		((DataChunkOutputStream) localObject1).writeUInt(this.imgHeight);

		((DataChunkOutputStream) localObject1).writeUInt(0L);
		((DataChunkOutputStream) localObject1).writeUInt(0L);
		((DataChunkOutputStream) localObject1).writeUInt(0L);
		((DataChunkOutputStream) localObject1).writeUInt(0L);

		this.strhChunk.seekToStartOfData();
		localObject1 = this.strhChunk.getOutputStream();
		((DataChunkOutputStream) localObject1).writeType("vids");

		switch (this.videoFormat) {
		case RAW:
			((DataChunkOutputStream) localObject1).writeType("DIB ");
			break;
		case JPG:
			((DataChunkOutputStream) localObject1).writeType("MJPG");
			break;
		case PNG:
		default:
			((DataChunkOutputStream) localObject1).writeType("png ");
		}

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUShort(0);

		((DataChunkOutputStream) localObject1).writeUShort(0);

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUInt(this.timeScale);

		((DataChunkOutputStream) localObject1).writeUInt(this.frameRate);

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUInt(this.videoFrames.size());

		((DataChunkOutputStream) localObject1).writeUInt(l);

		((DataChunkOutputStream) localObject1).writeInt(-1);

		((DataChunkOutputStream) localObject1).writeUInt(0L);

		((DataChunkOutputStream) localObject1).writeUShort(0);
		((DataChunkOutputStream) localObject1).writeUShort(0);
		((DataChunkOutputStream) localObject1).writeUShort(this.imgWidth);
		((DataChunkOutputStream) localObject1).writeUShort(this.imgHeight);

		this.strfChunk.seekToStartOfData();
		localObject1 = this.strfChunk.getOutputStream();
		((DataChunkOutputStream) localObject1).writeUInt(40L);

		((DataChunkOutputStream) localObject1).writeInt(this.imgWidth);

		((DataChunkOutputStream) localObject1).writeInt(this.imgHeight);

		((DataChunkOutputStream) localObject1).writeShort(1);

		((DataChunkOutputStream) localObject1).writeShort(24);

		switch (this.videoFormat) {
		case RAW:
			((DataChunkOutputStream) localObject1).writeInt(0);
			break;
		case JPG:
			((DataChunkOutputStream) localObject1).writeType("MJPG");
			break;
		case PNG:
		default:
			((DataChunkOutputStream) localObject1).writeType("png ");
		}

		((DataChunkOutputStream) localObject1).writeInt(this.imgWidth * this.imgHeight * 3);

		((DataChunkOutputStream) localObject1).writeInt(0);

		((DataChunkOutputStream) localObject1).writeInt(0);

		((DataChunkOutputStream) localObject1).writeInt(0);

		((DataChunkOutputStream) localObject1).writeInt(0);

		this.aviChunk.finish();
	}

	public void initVideoFrames() {
		this.videoFrames = new LinkedList();
	}

	public void setOutputFile(File paramFile) {
		try {
			this.out = new FileImageOutputStream(paramFile);
		} catch (FileNotFoundException localFileNotFoundException) {
			localFileNotFoundException.printStackTrace();
		} catch (IOException localIOException) {
			localIOException.printStackTrace();
		}
	}

	public void setVideoFormat(IAVIOutputStream.VideoFormat paramVideoFormat) {
		this.videoFormat = paramVideoFormat;
	}

	public void setSingleVideo(boolean paramBoolean) {
		this.singleVideo = paramBoolean;
	}

	public boolean isStreamClosed() {
		return this.streamClosed;
	}

	public void setStreamClosed(boolean paramBoolean) {
		this.streamClosed = paramBoolean;
	}
}

/*
 * Location:
 * D:\Users\tianlin\Downloads\JViewer-AVIStream.jar!\com\ami\kvm\jviewer\
 * avistream\AVIOutputStream.class Java compiler version: 5 (49.0) JD-Core
 * Version: 0.7.1
 */